#include "kcentergreedyint8.h"
#include "source/tools.h"
#include <QThread>
#include <QWriteLocker>
#include "pccoremodel.h"

KCenterGreedyInt8& KCenterGreedyInt8::instance() {
    static KCenterGreedyInt8 instance;
    return instance;
}

KCenterGreedyInt8::KCenterGreedyInt8(QObject* parent) : QObject(parent) {
    qInfo() << "KCenterGreedyInt8 initialized.";
    workerThread.start(QThread::LowPriority);
    this->moveToThread(&workerThread);
}

KCenterGreedyInt8::~KCenterGreedyInt8() {
    // 请求线程结束
    workerThread.quit();
    workerThread.wait();
}

int KCenterGreedyInt8::buildKnowledgeBase() {

    QVector<size_t> selectedIndices;
    selectedIndices.clear();
    QVector<int8_t> knowledgeTemp;
    minDistances_.resize(totalPoints);
    minDistances_.fill(std::numeric_limits<int32_t>::max(), totalPoints);
    knowledgeTemp.resize(N_ * D_);
    qInfo() << "knowledgeTemp.size() :" << knowledgeTemp.size();
    // 随机选择第一个点
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, static_cast<int>(totalPoints - 1));

    size_t idx = dis(gen);
    selectedIndices.append(idx);
    memcpy(knowledgeTemp.data(), embedding.data() + idx * D_, D_);

    minDistances_[idx] = 0;

    emit trianStartSignal();
    int lastProgress = -1;
    for (size_t i = 1; i < N_; i++) {
        updateDistances(idx);
        size_t newCenter = getNewIdx();

        if (newCenter >= totalPoints) {
            qCritical() << "Invalid newCenter index: " << newCenter << ", totalPoints: " << totalPoints;
            emit trianStartFinish();
            return -3;
        }

        // 检查 memory copy 范围是否合理
        //qInfo() << "Copying data: knowledgeTemp[" << i * D_ << "] <- embedding[" << newCenter * D_ << "] for " << D_ << " bytes";
        if ((i * D_ + D_) > knowledgeTemp.size() || (newCenter * D_ + D_) > embedding.size()) {
            qCritical() << "Memory access out of bounds!";
            emit trianStartFinish();
            return -4;
        }

        memcpy(knowledgeTemp.data() + i * D_, embedding.data() + newCenter * D_, D_);
        minDistances_[newCenter] = 0;
        selectedIndices.append(newCenter);
        idx = newCenter;

        // 计算进度并发出信号
        int progress = static_cast<int>((static_cast<double>(i) / N_) * 100);
        if (progress != lastProgress) {
            emit trianStartProcess(progress);
            lastProgress = progress;
        }
    }
    writeKnowledgeBase(knowledgeTemp);
    //saveQVectorToBinary(knowledgeTemp ,"corset1.dat");
    emit trianStartFinish();
     qInfo() << "Knowledge base built with size:" << N_;
    PCcoreModel::instance().finishexportEmbedding();
    return 0;

}

void KCenterGreedyInt8::addToKnowledgeBase(const QVector<int8_t>& newData) {
    qInfo() << "Adding new data to knowledge base...";
    if (newData.size() != 784 * D_) {
        qWarning() << "Invalid new data dimensions. Aborting.";
        return;
    }

    QVector<int8_t> tempBase = knowledgeBase_;
    tempBase.resize((N_ + 784) * D_);
    memcpy(tempBase.data() + N_ * D_, newData.data(), 784 * D_);

    embedding = tempBase;
    buildKnowledgeBase();
    qInfo() << "Knowledge base updated with new data.";
}

void KCenterGreedyInt8::updateDistances(size_t centerIdx) {
    const int8_t* center = embedding.data() + centerIdx * D_;
    for (size_t i = 0; i < totalPoints; i++) {
        if (minDistances_[i] == 0) continue; // 已是中心

        const int8_t* point = embedding.data() + i * D_;
        int32_t dist = computeDistanceSquared(point, center);
        if (dist < minDistances_[i]) {
            minDistances_[i] = dist;
        }
    }
}

size_t KCenterGreedyInt8::getNewIdx() {
    int32_t maxDist = -1;
    size_t maxIdx = 0;
    for (size_t i = 0; i < totalPoints; i++) {
        if (minDistances_[i] > maxDist) {
            maxDist = minDistances_[i];
            maxIdx = i;
        }
    }
    return maxIdx;
}

int32_t KCenterGreedyInt8::computeDistanceSquared(const int8_t* a, const int8_t* b) const {
    int32_t dist = 0;
    for (size_t d = 0; d < D_; d++) {
        int16_t diff = static_cast<int16_t>(a[d]) - static_cast<int16_t>(b[d]);
        dist += diff * diff;
    }
    return dist;
}

int KCenterGreedyInt8::addEmbedding(void* data, size_t num) {
    qInfo() << "Adding new embeddings...";
    if ((num) % D_ != 0) {
        qWarning() << "Invalid embedding dimensions. Must be a multiple of" << D_;
        return embedding.size() / D_ / onceN_;
    }

    int8_t* inputData = static_cast<int8_t*>(data);
    size_t originalSize = embedding.size();

    embedding.resize(originalSize + num);
    memcpy(embedding.data() + originalSize, inputData, num);

    qInfo() << "Added" << num << "new embeddings. Total embeddings:" << embedding.size() / D_;
    return embedding.size() / D_ / onceN_;
}

void KCenterGreedyInt8::writeKnowledgeBase(QVector<int8_t> &value) {
    QWriteLocker locker(&lock_);
    knowledgeBase_ = value;
}

// 读取函数
QVector<int8_t> &KCenterGreedyInt8::readKnowledgeBase() {
    QReadLocker locker(&lock_);
    return knowledgeBase_;
}

int KCenterGreedyInt8::startBuildKnowledgeBase()
{
    qInfo() << "Building knowledge base from large dataset : " << embedding.size() / D_;
    if (embedding.size() % D_ != 0) {
        qWarning() << "Invalid dataset dimensions. Aborting.";
        return -1;
    }
    if(embedding.size() / D_ < N_)
    {
        qWarning() << "Invalid dataset dimensions."
                      " (embedding.size() / D_. :"
                   << embedding.size() / D_;
        return -2;
    }

    totalPoints = embedding.size() / D_;
    qInfo() << "totalPoints :" << totalPoints;
    // 在主线程中调用，触发工作线程中的槽函数
    QMetaObject::invokeMethod(this, "buildKnowledgeBase", Qt::QueuedConnection);
    return 0;
}
